
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Drawing;
using Microsoft.VisualStudio.Modeling;
using Dslfactory.wcfLanguage.Properties;
using DslFactory.Utilities.DteCodeModel;
using EnvDTE;
using EnvDTE80;
using System.Collections;
namespace Dslfactory.wcfLanguage
{
 public partial class DataClassShapeBase
 {
  /// <summary>
  /// Represented Data class
  /// </summary>
  public DataClass DataClass
  {
   get
   {
    return ModelElement as DataClass;
   }
  }

  /// <summary>
  /// Is the shape filtered ?
  /// </summary>
  /// <returns></returns>
  internal bool GetIsFilteredValue()
  {
   foreach (FieldOrProperty m in DataClass.Members)
    if (m.Representation == RepresentationKind.Hidden)
     return true;
   return false;
  }
 }

 public partial class DataClassShape
 {
/*
  /// <summary>
  /// Changes the delegate used to obtain the icon of the Method in an Interface shape
  /// </summary>
  /// <param name="melType"></param>
  /// <returns></returns>
  protected override CompartmentMapping[] GetCompartmentMappings(System.Type melType)
  {
   CompartmentMapping[] mappings = base.GetCompartmentMappings(melType);
   ElementListCompartmentMapping operationMapping = mappings[0] as ElementListCompartmentMapping;
   operationMapping.ImageGetter = GetElementImage;
   operationMapping.ElementListGetter = GetElements;
   return mappings;
  }
*/

  /// <summary>
  /// Decides what the icon of the method will be in the interface shape
  /// </summary>
  /// <param name="mel"></param>
  /// <returns></returns>
  protected Image GetElementImage(ModelElement mel)
  {
   FieldOrProperty member = mel as FieldOrProperty;
   if (member == null)
    return null;

   switch (member.Kind)
   {
    case DataMemberKind.Field:
     switch (member.Visibility)
	    {
		    case Visibility.Public:
       return Resources.VSObject_Field;
      
      case Visibility.Protected:
        return Resources.VSObject_Field_Protected;
      
      case Visibility.Private:
        return Resources.VSObject_Field_Private;

      case Visibility.Internal:
        return Resources.VSObject_Field_Sealed;

      default:
        return Resources.VSObject_Field;
	    }

    case DataMemberKind.Property:
     switch (member.Visibility)
	    {
		    case Visibility.Public:
        return Resources.VSObject_Properties;
      
      case Visibility.Protected:
        return Resources.VSObject_Properties_Protected;
      
      case Visibility.Private:
        return Resources.VSObject_Properties_Private;

      case Visibility.Internal:
        return Resources.VSObject_Properties_Sealed;

      default:
        return Resources.VSObject_Properties;
     }

    default:
     return null;
   }
  }


  protected IList GetElements(ModelElement startElement)
  {
   DataClass dataClass = startElement as DataClass;
   System.Collections.Generic.List<FieldOrProperty> shownMembers = new System.Collections.Generic.List<FieldOrProperty>();
   foreach (FieldOrProperty m in dataClass.Members)
    if (m.Representation == RepresentationKind.AsMember)
     shownMembers.Add(m);
   return shownMembers ;
  }


  /// <summary>
  /// When double-clicking on a shape, we get to the type
  /// </summary>
  /// <param name="e"></param>
  public override void OnDoubleClick(DiagramPointEventArgs e)
  {
   KnownCodeTypes knownCodeTypes = KnownCodeTypes.FromStore(Store);
   if (knownCodeTypes == null)
    return;
   CodeType codeType = knownCodeTypes.GetFullNamedType(DataClass.FullName);
   if (codeType == null)
    return;

   foreach (ModelElement represented in e.HitDiagramItem.RepresentedElements)
   {
    ModelElement element = (represented is ShapeElement) ? (represented as ShapeElement).ModelElement : represented;
    if (element is DataClass)
     IncrementalGenerator.TryToShow(codeType as CodeElement);
    else if (element is FieldOrProperty)
    {
     CodeElement member = IncrementalGenerator.FindFirstMember(codeType as CodeClass2, (element as FieldOrProperty).Name);
     if (member != null)
      IncrementalGenerator.TryToShow(member as CodeElement);

    }
   }
  }
 }

 [RuleOn(typeof(FieldOrProperty), InitiallyDisabled=true)]
 internal class RepresentationChangedRefreshesComparmentsOrRole : ChangeRule
 {
  public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
  {
   FieldOrProperty member = e.ModelElement as FieldOrProperty;
   DataClass contract = member.DataClass as DataClass;
   if (contract == null)
    return;

   if (e.DomainProperty.Id == FieldOrProperty.RepresentationDomainPropertyId)
   {
    
    // Update 
    RepresentationKind oldRepresentation = (RepresentationKind)e.OldValue;
    RepresentationKind newRepresentation = (RepresentationKind)e.NewValue;

    switch (oldRepresentation)
    {
     case RepresentationKind.AsMember:
      // Update compartment since the representation is no longer as a member
      UpdateCompartment(contract);

      // Create a relationship if necessary
      if (newRepresentation == RepresentationKind.AsRole)
      {
       ShowAsRole(member);
      }
     break;

     case RepresentationKind.AsRole:
      // Remove relationship
      if (member.Relationship != null)
       member.Relationship.Delete();

      if (newRepresentation == RepresentationKind.AsMember)
       UpdateCompartment(contract);

     break;
     case RepresentationKind.Hidden:
      if (newRepresentation == RepresentationKind.AsMember)
       UpdateCompartment(contract);
      else
       ShowAsRole(member);

      break;
     default:
      break;
    }
    
   }
  }


  /// <summary>
  /// Presents the member as a relationship role
  /// </summary>
  /// <param name="member"></param>
  private static void ShowAsRole(FieldOrProperty member)
  {
   if (member.Relationship != null)
    return;

   DataClass target ;

   if (member.MemberFullTypeName.EndsWith("[]"))
    target = member.DataClass.ServicesDefinition.FindDataContractOfFullName(member.MemberFullTypeName.Replace("[]", ""));
   else
    target = member.DataClass.ServicesDefinition.FindDataContractOfFullName(member.MemberFullTypeName);

    if (target != null)
    {
     // Add a member, but do not fire the rule
     member.Store.RuleManager.DisableRule(typeof(UsageAddedAddsMemberRule));
     DataClassUsesDataClasses usage = new DataClassUsesDataClasses(member.Store,
      new RoleAssignment(DataClassUsesDataClasses.UsedDataClassDomainRoleId, target),
      new RoleAssignment(DataClassUsesDataClasses.UsingDataClassDomainRoleId, member.DataClass));
     usage.RepresentedDataMember = member;
     member.Store.RuleManager.EnableRule(typeof(UsageAddedAddsMemberRule));
    }
    else
     member.Representation = RepresentationKind.AsMember;
  }

  private static void UpdateCompartment(DataClass contract)
  {
   CompartmentItemAddRule.UpdateCompartments(contract.ServicesDefinition.DataClasses, typeof(DataClassShape), "Data", false);
  }
 }

 [RuleOn(typeof(DataClassUsesDataClasses), InitiallyDisabled=true)]
 class UsageAddedAddsMemberRule : AddRule
 {
  public override void ElementAdded(ElementAddedEventArgs e)
  {
   DataClassUsesDataClasses usage = e.ModelElement as DataClassUsesDataClasses;
   if ((usage.UsingDataClass == null) || (usage.UsedDataClass == null))
    return;

   // Added by "Representation" property
   if (usage.RepresentedDataMember != null)
    return;

   FieldOrProperty member = usage.UsingDataClass.CreateMember(usage.UsedDataClass.FullName);
   member.DataClass = usage.UsingDataClass;
   usage.RepresentedDataMember = member;

   // Create a name from the type name, but ensuring it is unique
   string simpleName = VariableLabel.SimplifyTypeName(KnownCodeTypes.FromStore(usage.Store), usage.UsedDataClass.FullName);
   if (simpleName.Length>1)
    simpleName = char.ToLower(simpleName[0])+simpleName.Substring(1);
   SetUniqueName(member, simpleName);

   // Set the remaining information
   member.Representation = RepresentationKind.AsRole;                             
  }


  /// <summary>
  /// Set the unique name to the member
  /// </summary>
  /// <param name="member"></param>
  /// <param name="simpleName"></param>
  private void SetUniqueName(FieldOrProperty member, string simpleName)
  {
   if (member.DataClass.FindMemberOfName(simpleName) != null)
   {
    int index = 1;
    while (member.DataClass.FindMemberOfName(simpleName + index.ToString()) != null)
     index++;
    member.Name = simpleName + index.ToString();
   }
   else
    member.Name = simpleName;
  }
 }

 [RuleOn(typeof(DataClassUsesDataClasses))]
 class UsageRemovedChangesRepresentationRule : DeletingRule
 {
  public override void ElementDeleting(ElementDeletingEventArgs e)
  {
   DataClassUsesDataClasses usage = e.ModelElement as DataClassUsesDataClasses;
   if (usage.UsingDataClass.IsDeleted
    || usage.UsingDataClass.IsDeleting
    || usage.UsedDataClass.IsDeleting
    || usage.UsedDataClass.IsDeleted)
    return;

   if (usage.RepresentedDataMember == null)
    return;

   if (usage.RepresentedDataMember.Representation == RepresentationKind.AsRole)
    usage.RepresentedDataMember.Representation = RepresentationKind.AsMember;
  }
 }
}
